use std::net::ToSocketAddrs;
use std::thread;
use std::time::Duration;

use iced::{Color, Column, Container, Element, HorizontalAlignment, Length, Scrollable, Subscription, Text, TextInput, scrollable, text_input};
use moyu_chat_core::client::Client;
use moyu_chat_core::client::MessageSource;
use moyu_chat_core::server::Server;
use moyu_chat_core::utils;

use crate::ui::consts;
use crate::ui::styles;

use super::{ScreenState, cmd::Cmd, recv::recv_msg};

static mut SHOULD_CLOSE_SERVER: bool = true;

#[derive(Default)]
pub struct Chat {
    client: Option<Client>,

    title: String,
    recv_addr: String,
    server_addr: Option<String>,
    send_value: String,
    pop_message: PopMessage,

    scroll: scrollable::State,
    pop_scroll: scrollable::State,
    send: text_input::State,
}

#[derive(Default)]
struct PopMessage {
    color: Color,
    content: String,
}

impl PopMessage {
    fn set_error(&mut self, msg: &str) {
        self.color = Color::from_rgb(1.0, 0.0, 0.0);
        self.content.clear();
        self.content.insert_str(0, msg);
        self.content.insert_str(0, "Error: ");
    }

    fn set_msg(&mut self, msg: &str) {
        self.color = Color::BLACK;
        self.content.clear();
        self.content.insert_str(0, msg);
    }

    #[allow(dead_code)]
    fn set_success(&mut self, msg: &str) {
        self.color = Color::from_rgb(0.0, 1.0, 0.0);
        self.content.clear();
        self.content.insert_str(0, msg);
    }
}

#[derive(Debug, Clone)]
pub enum Message {
    InputChanged(String),
    InputSend,
    GetMessage(Option<moyu::Packet>),
}

impl Chat {
    pub fn new(recv_addr: String) -> Self {
        Self {
            title: "Message".into(),
            recv_addr,
            ..Default::default()
        }
    }

    pub fn recv_addr(&self) -> String {
        self.recv_addr.clone()
    }

    pub fn set_client(&mut self, client: Client) {
        self.client.replace(client);
    }

    pub fn subscription(&self) -> Subscription<Message> {
        recv_msg(0, self.client.as_ref().unwrap().socket()).map(Message::GetMessage)
    }

    pub fn title(&self) -> String {
        format!(
            "{} - {}",
            consts::APP_NAME,
            if let Some(client) = &self.client {
                client.name()
            } else {
                "ERROR".into()
            }
        )
    }

    fn process_room_cmd(&mut self, cmd: Cmd) {
        let mut pms = cmd.params.iter();
        if let Some(p) = pms.next() {
            match p.as_str() {
                "j" => {
                    if let Some(addr) = pms.next() {
                        println!("{}", addr);
                        let addr = addr.trim();
                        if let Some(ref mut client) = self.client {
                            client.join(addr.to_socket_addrs().unwrap().next().unwrap(), self.recv_addr.to_socket_addrs().unwrap().next().unwrap()).unwrap();
                        }
                    }
                }
                "c" => {
                    let addr = utils::new_local_addr();
                    self.server_addr.replace(addr.to_string());
                    
                    unsafe {
                        SHOULD_CLOSE_SERVER = false;
                    }

                    thread::spawn(move || {
                        let mut server = Server::from_addr(addr);
                        
                        while !unsafe { SHOULD_CLOSE_SERVER } {
                            server.update();
                        }
                    });
                    
                    thread::sleep(Duration::from_secs_f32(0.02));

                    if let Some(ref mut client) = self.client {
                        client.join(addr, self.recv_addr.to_socket_addrs().unwrap().next().unwrap()).unwrap();
                    }
                }
                "s" => {
                    if let Some(_) = &self.server_addr {
                        self.server_addr.take();
                        if let Some(ref mut client) = self.client {
                            client.disconnect();
                        }

                        unsafe {
                            SHOULD_CLOSE_SERVER = true;
                        }
                    }
                }
                "exit" => {
                    if let Some(ref mut client) = self.client {
                        client.exit();
                    }
                }
                _ => { }
            }
        }
    }

    pub fn update(&mut self, msg: Message) -> ScreenState {
        match msg {
            Message::InputChanged(val) => self.send_value = val,
            Message::InputSend => {
                if let Ok(cmd) = Cmd::new(self.send_value.trim()) {
                    println!("Find cmd [{}]:{:?}", cmd.name, cmd.params);
                    let mut pms = cmd.params.iter();
                    match &cmd.name[..] {
                        "c" => {
                            if let Some(p) = pms.next() {
                                match p.as_str() {
                                    "self" => {
                                        if let Some(ref mut client) = self.client {
                                            client.connect(self.recv_addr.parse().unwrap());
                                        }
                                    }
                                    _ => {
                                        if let Some(ref mut client) = self.client {
                                            client.connect(p.parse().unwrap());
                                        }
                                    }
                                }
                            } else {
                                self.pop_message.set_error("No parameters");
                            }
                        }
                        
                        "h" => {
                            self.pop_message.set_msg(consts::HELP.into());
                        }
                        "q" => {
                            if let Some(ref mut client) = self.client {
                                if let Err(code) = client.query() {
                                    self.pop_message.set_error(code);
                                }
                            }
                        }
                        "t" => {
                            if let Some(ref mut client) = self.client {
                                if let Some(name) = pms.next() {
                                    if let Some(msg) = pms.next() {
                                        if let Err(msg) = client.tell(name, msg) {
                                            self.pop_message.set_error(msg);
                                        }
                                    } else {
                                        self.pop_message.set_error("Request message");
                                    }
                                } else {
                                    self.pop_message.set_error("Request name");
                                }
                            } else {
                                self.pop_message.set_error("No Client");
                            }
                        }
                        "room" => {
                            self.process_room_cmd(cmd);
                        }
                        "exit" => {
                            unsafe { SHOULD_CLOSE_SERVER = true; }
                            if let Some(ref mut client) = self.client {
                                client.exit();
                            }
                            return ScreenState::Exit;
                        }
                        _ => {}
                    }
                } else if !self.send_value.trim().is_empty() {
                    if let Some(ref mut client) = self.client {
                        if let Err(msg) = client.send_message(&self.send_value) {
                            self.pop_message.set_error(msg.into())
                        }
                    }
                }

                self.send_value.clear();
            },
            Message::GetMessage(pak) => {
                if let Some(pak) = pak {
                    match pak.get_op() {
                        moyu::protocol::Operator::TransmitMessage | moyu::protocol::Operator::SendMessage => {
                            if let Some(ref mut client) = self.client {
                                client.recv_packet(pak);
                            }
                        }
                        moyu::protocol::Operator::Reply => {
                            let mut msg = pak.get_message().replace(",", "\n").trim().to_string();
                            let cnt_user = msg.lines().count();
                            msg.insert_str(0, &format!("{} user(s) online.\n", cnt_user));

                            self.pop_message.set_msg(&msg)
                        }
                        _ => ()
                    }
                }
            },
        }

        ScreenState::Keep
    }

    pub fn view(&mut self) -> Element<Message> {
        let title = Text::new(&self.title)
            .width(Length::Fill)
            .size(32)
            .horizontal_alignment(iced::HorizontalAlignment::Center);

        let mut message_panel = Scrollable::new(&mut self.scroll)
            .scrollbar_width(1)
            .scroller_width(1)
            .width(Length::Fill);

        if let Some(ref client) = self.client {
            let msgs = client.messages();
            let front = &msgs[(0.max(msgs.len() as i32 - 20)) as usize..];
            message_panel = front.iter().rev().fold(message_panel, |p, msg| {
                let (ha, color) = if msg.source == MessageSource::User {
                    (HorizontalAlignment::Right, consts::colors::USER_SELF)
                } else {
                    (HorizontalAlignment::Left, consts::colors::USER_OTHER)
                };

                let block = Column::new()
                    .padding(5)
                    .push(Text::new(&msg.name).color(color).width(Length::Fill).horizontal_alignment(ha)) 
                    .push(Text::new(&msg.message).width(Length::Fill).horizontal_alignment(ha))
                    .spacing(2)
                    .push(Text::new(format!("-{}-", msg.timestamp)).size(12).color(consts::colors::TIMESTAMP).width(Length::Fill).horizontal_alignment(HorizontalAlignment::Center));

                let bc = Container::new(block)
                    .style(styles::MessagePanel);

                p.push(bc).spacing(5)
            })
        }

        let msg_group = Column::new()
            .height(Length::FillPortion(2))
            .push(title)
            .spacing(10)
            .push(message_panel);

        let input = TextInput::new(
            &mut self.send,
            consts::HINT,
            &self.send_value,
            Message::InputChanged,
        )
        .padding(3)
        .on_submit(Message::InputSend);

        
        let pop = {
            let color = self.pop_message.color;
            self.pop_message.content
                .split("\n")
                .fold(Column::new(), |col, line| {
                    let line = Text::new(line)
                        .color(color);
                    col.push(line)
                })
        };

        let layout = Column::new()
            .padding(20)
            .push(Text::new(format!("Local Address: {}", self.recv_addr)))
            .push(Text::new(if let Some(server) = &self.server_addr { format!("Room host: {}", server) } else { "Not a room host".into() }))
            .push(msg_group)
            .push(input)
            .spacing(2)
            .push(Scrollable::new(&mut self.pop_scroll).width(Length::Fill).height(Length::Units(75)).push(pop));

        Container::new(layout).center_y().into()
    }
}
